<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="rZiwG" id="rZiwG"><span data-lake-id="ub6e38401" id="ub6e38401">典型回答</span></h1>
  <p data-lake-id="u9fc2f833" id="u9fc2f833"><br></p>
  <p data-lake-id="u778570aa" id="u778570aa"><br></p>
  <p data-lake-id="u79e9ee32" id="u79e9ee32"><span data-lake-id="uccb76348" id="uccb76348">乐观锁和悲观锁是在处理并发访问时使用的两种不同的策略。</span></p>
  <p data-lake-id="u4ef240e9" id="u4ef240e9"><span data-lake-id="u9013652d" id="u9013652d">​</span><br></p>
  <p data-lake-id="udc5cafb6" id="udc5cafb6"><strong><span data-lake-id="u77d3222e" id="u77d3222e">乐观锁的基本思想是假设冲突很少发生</span></strong><span data-lake-id="u7a066b33" id="u7a066b33">，每个线程在修改数据之前，先获取一个版本号或时间戳，并在更新时检查这个版本号或时间戳，以确保其他线程没有同时修改数据。</span></p>
  <p data-lake-id="u7b93966c" id="u7b93966c"><span data-lake-id="ua7438669" id="ua7438669">​</span><br></p>
  <p data-lake-id="u34a18dd1" id="u34a18dd1"><strong><span data-lake-id="u10ef920f" id="u10ef920f">乐观锁适用于读操作频繁，写操作相对较少的场景。</span></strong><span data-lake-id="u30eb4be0" id="u30eb4be0">当冲突较少，且并发写入的概率较低时，乐观锁的性能可能更好。</span></p>
  <p data-lake-id="u3dcb69f4" id="u3dcb69f4"><span data-lake-id="u6b124465" id="u6b124465">​</span><br></p>
  <p data-lake-id="u88599feb" id="u88599feb"><strong><span data-lake-id="u60eb4ad4" id="u60eb4ad4">悲观锁则是假设冲突经常发生</span></strong><span data-lake-id="u85d24850" id="u85d24850">，因此在访问共享资源之前，线程会先获取锁，确保其他线程无法同时访问相同的数据。这可能导致并发性降低，因为只有一个线程能够访问数据。</span></p>
  <p data-lake-id="ud461b352" id="ud461b352"><br></p>
  <p data-lake-id="u3f85bdd9" id="u3f85bdd9"><strong><span data-lake-id="u669ecc87" id="u669ecc87">悲观锁适用于写操作较为频繁，且并发写入的概率较高的场景。</span></strong><span data-lake-id="uec290489" id="uec290489">悲观锁可以有效地避免多个线程同时修改相同数据的情况。</span></p>
  <p data-lake-id="ubf075453" id="ubf075453"><span data-lake-id="u274e6b0f" id="u274e6b0f">​</span><br></p>
  <p data-lake-id="u5c81ce83" id="u5c81ce83"><span data-lake-id="u417ec52c" id="u417ec52c">乐观锁和悲观锁还有个区别：乐观锁因为比较乐观，所以一般是先做业务逻辑操作，比如参数处理，内存中进行模型组装调整，然后再去更新数据库。悲观锁因为比较悲观，所以会先尝试加锁，然后再去做业务逻辑操作。</span></p>
  <p data-lake-id="u01eeebd2" id="u01eeebd2"><span data-lake-id="uf9c3a74c" id="uf9c3a74c">​</span><br></p>
  <p data-lake-id="ubd5f32f4" id="ubd5f32f4"><span data-lake-id="u1abf65fe" id="u1abf65fe">也就是说，乐观锁是先干活，后加锁。悲观锁是先加锁，再干活。</span></p>
  <p data-lake-id="u50431baa" id="u50431baa"><span data-lake-id="u3b5230a4" id="u3b5230a4">​</span><br></p>
  <p data-lake-id="u6672b9ae" id="u6672b9ae"><span data-lake-id="u849f4dbd" id="u849f4dbd">而高并发的写操作时，你干了一大堆活，把模型都组装好了，内存计算也都做完了，结果最后去数据库那更新的时候发现版本号变了。这不是大冤种吗？</span></p>
  <p data-lake-id="ueef65987" id="ueef65987"><span data-lake-id="ue9950aa1" id="ue9950aa1">​</span><br></p>
  <p data-lake-id="ud3c801e7" id="ud3c801e7"><span data-lake-id="u874fc2d5" id="u874fc2d5">所以，应该是先尝试获取锁，如果获取锁成功，再进行业务操作，否则就直接返回失败。这样可以做fail-fast。</span></p>
  <p data-lake-id="ufac6fd2d" id="ufac6fd2d"><br></p>
  <p data-lake-id="u57e76d0c" id="u57e76d0c"><span data-lake-id="u1c3310e6" id="u1c3310e6">综上，在高并发场景中，一般来说</span><strong><span data-lake-id="ucfd2e690" id="ucfd2e690">并发写入的冲突较为频繁，所以建议优先考虑悲观锁。</span></strong><span data-lake-id="u5f5b1aae" id="u5f5b1aae">即在做并发操作前，先尝试获取锁，如果获取锁成功，在进行业务操作，否则就直接返回失败。</span></p>
  <p data-lake-id="u5771dd52" id="u5771dd52"><span data-lake-id="u9d903bcc" id="u9d903bcc">​</span><br></p>
  <p data-lake-id="u89abc965" id="u89abc965"><span data-lake-id="ucd80e041" id="ucd80e041">比如，我们通常在并发场景下都使用分布式锁，即先加分布式锁，然后再操作。这个就是一个悲观锁的思想，我认为冲突一定很大，所以我先尝试加锁。拿到锁再开始干活。</span></p>
  <p data-lake-id="ua173b057" id="ua173b057"><span data-lake-id="u7e3868b1" id="u7e3868b1">​</span><br></p>
  <h1 data-lake-id="QCP2N" id="QCP2N"><span data-lake-id="uf90e938c" id="uf90e938c">扩展知识</span></h1>
  <p data-lake-id="ud72b37c9" id="ud72b37c9"><br></p>
  <h2 data-lake-id="gxCax" id="gxCax"><span data-lake-id="ucce8a84a" id="ucce8a84a">乐观锁并非无锁</span></h2>
  <p data-lake-id="u6a72cd57" id="u6a72cd57"><br></p>
  <p data-lake-id="u1aa2cc20" id="u1aa2cc20"><span data-lake-id="uae0055e0" id="uae0055e0">不管是乐观锁还是悲观锁，都并不是无锁的。这一点一定要注意。</span></p>
  <p data-lake-id="u39acbe97" id="u39acbe97"><br></p>
 </body>
</html>